/*
 * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef OHOS_SHARING_RTMP_AMF_H
#define OHOS_SHARING_RTMP_AMF_H

#include <functional>
#include <map>
#include <string>
#include <vector>
#include "data_buffer.h"

namespace OHOS {
namespace Sharing {

enum AmfType {
    AMF_NUMBER,
    AMF_INTEGER,
    AMF_BOOLEAN,
    AMF_STRING,
    AMF_OBJECT,
    AMF_NULL,
    AMF_UNDEFINED,
    AMF_ECMA_ARRAY,
    AMF_STRICT_ARRAY,
};

class AmfValue {
public:
    friend class AMFEncoder;

    using MapType = std::map<std::string, AmfValue>;
    using ArrayType = std::vector<AmfValue>;

    AmfValue(int i);
    AmfValue(bool b);
    AmfValue(double n);
    AmfValue(const char *s);
    AmfValue(const std::string &s);
    AmfValue(const AmfValue &from);
    AmfValue(AmfType type = AMF_NULL);
    ~AmfValue();

    AmfValue &operator=(const AmfValue &from);
    const AmfValue &operator[](const char *str) const;

    explicit operator bool() const;

    void Clear();
    AmfType Type() const;

    int AsInteger() const;
    bool AsBoolean() const;
    double AsNumber() const;
    const std::string &AsString() const;

    std::string ToString() const;
    void Add(const AmfValue &val);
    void Set(const std::string &s, const AmfValue &val);
    void ObjectForEach(const std::function<void(const std::string &key, const AmfValue &val)> &fun) const;

private:
    void Init();
    void Destroy();
    const MapType &GetMap() const;
    const ArrayType &GetArr() const;

private:
    AmfType _type;
    union {
        bool boolean;
        int integer;
        double number;

        std::string *string;

        MapType *object;
        ArrayType *array;
    } _value{};
};

class AMFDecoder {
public:
    AMFDecoder(const DataBuffer &buf, size_t pos, int version = 0);

    template <typename TP>
    TP Load();

private:
    uint8_t Front();
    uint8_t PopFront();

    std::string LoadKey();

    AmfValue LoadArr();
    AmfValue LoadEcma();
    AmfValue LoadObject();

private:
    int version_;
    size_t pos_;

    DataBuffer buffer_;
};

class AMFEncoder {
public:
    AMFEncoder &operator<<(int n);
    AMFEncoder &operator<<(bool b);
    AMFEncoder &operator<<(double n);
    AMFEncoder &operator<<(const char *s);
    AMFEncoder &operator<<(std::nullptr_t);
    AMFEncoder &operator<<(const std::string &s);
    AMFEncoder &operator<<(const AmfValue &value);

    void Clear();
    const std::string &Data() const;

private:
    AMFEncoder &WriteUndefined();
    void WriteKey(const std::string &s);

private:
    std::string buf;
};

} // namespace Sharing
} // namespace OHOS

#endif // OHOS_SHARING_RTMP_AMF_H
